
// <copyright file="BindingListObservableCollection.cs" company="Subhadra Software Solutions">
//   
// </copyright>
// <summary>
//   The binding list observable collection.
// </summary>

namespace SubhadraSolutions.Sharp.Utils.Collections.ObjectModel
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.ComponentModel;

    using SubhadraSolutions.Sharp.Utils.Collections.Generic;
    using SubhadraSolutions.Sharp.Utils.Reflection;

    /// <summary>
    /// The binding list observable collection.
    /// </summary>
    /// <typeparam name="T">
    /// </typeparam>
    public class BindingListObservableCollection<T> : ObservableCollection<T>, IBindingList
        where T : new()
    {
        #region Fields

        /// <summary>
        /// The sync lock.
        /// </summary>
        private readonly object syncLock = new object();

        /// <summary>
        /// The is sorted.
        /// </summary>
        private bool isSorted;

        /// <summary>
        /// The sort direction.
        /// </summary>
        private ListSortDirection sortDirection;

        /// <summary>
        /// The sort property.
        /// </summary>
        private PropertyDescriptor sortProperty;

        #endregion Fields

        #region Constructors and Finalizers

        /// <summary>
        /// Initializes a new instance of the <see cref="BindingListObservableCollection{T}"/> class.
        /// </summary>
        public BindingListObservableCollection()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BindingListObservableCollection{T}"/> class.
        /// </summary>
        /// <param name="elements">
        /// The elements.
        /// </param>
        public BindingListObservableCollection(IEnumerable<T> elements)
        {
            foreach (T element in elements)
            {
                this.Add(element);
            }
        }

        #endregion Constructors and Finalizers

        #region Public Events

        /// <summary>
        /// The list changed.
        /// </summary>
        public event ListChangedEventHandler ListChanged;

        #endregion Public Events

        #region Public Properties

        /// <summary>
        /// Gets a value indicating whether allow edit.
        /// </summary>
        public bool AllowEdit
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets a value indicating whether allow new.
        /// </summary>
        public bool AllowNew
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets a value indicating whether allow remove.
        /// </summary>
        public bool AllowRemove
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets a value indicating whether is sorted.
        /// </summary>
        public bool IsSorted
        {
            get
            {
                return this.isSorted;
            }
        }

        /// <summary>
        /// Gets the sort direction.
        /// </summary>
        public ListSortDirection SortDirection
        {
            get
            {
                return this.sortDirection;
            }
        }

        /// <summary>
        /// Gets the sort property.
        /// </summary>
        public PropertyDescriptor SortProperty
        {
            get
            {
                return this.sortProperty;
            }
        }

        /// <summary>
        /// Gets a value indicating whether supports change notification.
        /// </summary>
        public bool SupportsChangeNotification
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets a value indicating whether supports searching.
        /// </summary>
        public bool SupportsSearching
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets a value indicating whether supports sorting.
        /// </summary>
        public bool SupportsSorting
        {
            get
            {
                return true;
            }
        }

        #endregion Public Properties

        #region Public Methods and Operators

        /// <summary>
        /// The add index.
        /// </summary>
        /// <param name="property">
        /// The property.
        /// </param>
        public void AddIndex(PropertyDescriptor property)
        {
        }

        /// <summary>
        /// The add new.
        /// </summary>
        /// <returns>
        /// The <see cref="object"/>.
        /// </returns>
        public object AddNew()
        {
            int index;
            var t = new T();
            lock (this.syncLock)
            {
                this.Add(t);
                index = this.Count - 1;
            }

            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, t));
            this.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, index));
            return t;
        }

        /// <summary>
        /// The apply sort.
        /// </summary>
        /// <param name="property">
        /// The property.
        /// </param>
        /// <param name="direction">
        /// The direction.
        /// </param>
        public void ApplySort(PropertyDescriptor property, ListSortDirection direction)
        {
            // Get list to sort
            // Note: this.Items is a non-sortable ICollection<T>
            var items = this.Items as List<T>;

            // Apply and set the sort, if items to sort
            if (items != null)
            {
                var comparer = DynamicComparerHelper<T>.GetComparerForProperty(property.Name);
                if (comparer == null)
                {
                    return;
                }
                
                if (direction == ListSortDirection.Descending)
                {
                    comparer = new InverseComparer<T>(comparer);
                }

                items.Sort(comparer);
                this.isSorted = true;
            }
            else
            {
                this.isSorted = false;
            }

            this.sortProperty = property;
            this.sortDirection = direction;

            this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        /// <summary>
        /// The find.
        /// </summary>
        /// <param name="property">
        /// The property.
        /// </param>
        /// <param name="key">
        /// The key.
        /// </param>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        public int Find(PropertyDescriptor property, object key)
        {
            // Specify search columns
            if (property == null)
            {
                return -1;
            }

            // Get list to search
            IList<T> items = this.Items;

            // Traverse list for value
            foreach (T item in items)
            {
                if (item != null)
                {
                    // Test column search value
                    var value = (string)property.GetValue(item);

                    // If value is the search value, return the
                    // index of the data item
                    if ((string)key == value)
                    {
                        return this.IndexOf(item);
                    }
                }
            }

            return -1;
        }

        /// <summary>
        /// The remove index.
        /// </summary>
        /// <param name="property">
        /// The property.
        /// </param>
        public void RemoveIndex(PropertyDescriptor property)
        {
        }

        /// <summary>
        /// The remove sort.
        /// </summary>
        public void RemoveSort()
        {
            this.isSorted = false;
            this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        #endregion Public Methods and Operators

        #region Methods

        /// <summary>
        /// The on list changed.
        /// </summary>
        /// <param name="e">
        /// The e.
        /// </param>
        protected virtual void OnListChanged(ListChangedEventArgs e)
        {
            if (this.ListChanged != null)
            {
                this.ListChanged(this, e);
            }
        }

        #endregion Methods
    }
}